/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief MaCI - SQL Interface Client header
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 * $Id: SQLClient.hpp,v 1.6 2009-08-05 06:09:00 amaula Exp $
 *
 */
#ifndef _MACI_INTERFACE_SQLCLIENT_HPP_
#define _MACI_INTERFACE_SQLCLIENT_HPP_

#include "MaCI.hpp"
#include "thread.hpp"
#include "sync.hpp"
#include "SQLResult.hpp"
#include "SQLQuery.hpp"

// These are not necessarily required here, but defined to make the
// interface easier to include.
#include "SQLData.hpp"

// Forward declare gimi::GIMI
namespace gimi {
  class GIMI;
}

namespace MaCI {
  namespace SQL {
    
    // Forward declaration.
    class CSQLData;
    class CSQLClient;

    
    /** Callback interface class for SQL interface.
     *
     * This interface defines functions which can be used for
     * SQLClient callbacks. These functions are executed (more
     * detail per function basis) as new data arrives, and directly
     * from the context of the receiving thread. <b>Therefore, it is
     * important that the handling function is as simple and fast as
     * possible, as it will block the execution of the receiving
     * thread while executed.</b>
     */
    class CSQLClientCallback
    {
    public:

      CSQLClientCallback() {}
      virtual ~CSQLClientCallback() {}


      /** Handler function for SQLData event.
       *
       * This function is called when the SQLClient receives new
       * data from the server.
       *
       * \note <b>Keep this function simple & fast as it will block the 
       * data receiving thread while executing!</b>
       *
       * @param[in] aData       Constant reference to CSQLData element 
       *                        containing the received event.
       * @param[in] aDataSequence Sequence number of the received data. 
       *                        This should increment by one on each call.
       * @param[in] aSQLClient Reference to SQLClient 
       *                        instance which received this event.
       */
      virtual void OnSQLDataEvent(const CSQLData &aData, 
                                    const unsigned int aDataSequence,
                                    CSQLClient &aSQLClient) = 0;
    };

    /** SQL event types.
     */
    enum ESQLEvent {
      KEventSQLData = 0 ///< SQLData element received and stored.
    };


    /** MaCI - SQL Client interface.
     * 
     * This class contains the methods and types for using
     * a MaCI::SQL service in the GIMnet.
     */
    class CSQLClient 
      : private gim::CSync,
        private gim::CThread,
        public MaCI::CMaCI
    {
    public:
      /** Constructor.
       * 
       * Constructor of the SQLClient. This requires;
       * a pointer to Initialized and connected GIMI, minor
       * number of this interface (Used as GIMI minor number)
       *
       * @param[in] aGIMIPtr Pointer to GIMI used for communication
       * @param[in] aInterfaceMinor Interface minor number. Usually
       *            used to indicate the instance of devices of same
       *            type. Can be left out if MaCISL is used.
       */     
      CSQLClient(gimi::GIMI *aGIMIPtr, const int aInterfaceMinor = -1);

      
      /** Destructor.
       */
      ~CSQLClient();


      /** Execute SQL Query synchronously.
       *
       * This function attempts to execute a SQL query
       * synchronously. This means that the function will sit back and
       * wait until a reply is available or time limit is hit.
       *
       * \note This function is implemented as calling the SendQuery()
       * and GetQueryResult() in sequence, and gives no performance
       * benefits (or hit) for using it instead.
       *
       * @param[out] aResult    Resulting data is stored here.
       * @param[in] aQuery      SQL query to execute
       * @param[in] aTimeout_ms Time allowed for this operation
       * @return                'true' if SendQuery() and GetQueryResult() both
       *                        return 'true', otherwise 'false'.
       */
      bool ExecuteQuery(CSQLResult &aResult,
                        const CSQLQuery &aQuery,
                        const unsigned int aTimeout_ms = 3000);


      /** Send SQL Query (Asynchronous)
       *  
       * @param[in] aQuery      SQL statement to execute.
       * @param[out] aQueryID   Query ID is stored here, existing value is 
       *                        ignored and overwritten.
       * @return                'true' when sent succesfully, 'false' on
       *                        container or 'Send' error.
       */
      bool SendQuery(const CSQLQuery &aQuery,
                     unsigned int &aQueryID);
      
      
      /** Get reply for a previously sent Query.
       *
       * This function inspects the Synchronous reply queue, and
       * attempts to find a reply for a previously sent query.
       *
       * \todo FIXME: Add feature to look for ANY reply (with any ID)
       *
       * @param[out] aSQLResult Converted SQL Query result is stored here.
       * @param[in] aQueryID    QueryID to look for.
       * @param[in] aTimeout_ms Timeout in milliseconds to wait for 
       *                        the operation to complete. Please note that
       *                        if this time is very short, and the reply is
       *                        received after that, the reply will
       *                        stay in the received buffer forever.
       * @return                'true' if all following conditions are met:
       *                        1) Got query in time 2) query contained replydata
       *                        3) Replydata was valid (decodeable) SQLResult
       *                        data.
       */
      bool GetQueryResult(CSQLResult &aSQLResult,
                          const unsigned int aQueryID,
                          const unsigned int aTimeout_ms = 2000);
      
      
      /** Assigns the SQL client callback function. 
       *
       * This sets a function object for calling a position client
       * callback. If this is set, the interface calls the assigned
       * functions immediately when event is available.
       * 
       * \note As the function is run in the context of the receiving
       * thread, user must make sure that the callback function is
       * fast. As if the function executes too long, receiving events
       * is interrupted.
       *
       * @param[in] aSQLClientCallback Pointer to class implementing the interface
       *                                 CSQLClientCallback.
       */
      void SetSQLClientCallback(CSQLClientCallback *aSQLClientCallback) {
        iSQLClientCallback = aSQLClientCallback;
      }

    protected:
      /** Open client interface.
       *
       * Implementation of Open() for the class. This function is
       * called by the base class CMaCI::Open() function.
       */
      EMaCIError DoOpen(void);
      
      
      /** Close client interface.
       *
       * Implementation of Close() for the class. This function is
       * called by the base class CMaCI::Close() function.
       */
      EMaCIError DoClose(void);


    private:
      /// Thread runner function. Is in charge of receiving events.
      int ThreadFunction(const int aThreadNumber);
      int ProcessMessage(const gimi::GIMIMessage &msg);

      //data.
      unsigned int iSQLDataSequence; ///< Received data sequence
      unsigned int iSQLCommandSequence; ///< Last sent Command sequence.
      CSQLData iLastSQLData; ///< Last element available
      CSQLClientCallback *iSQLClientCallback; ///< Pointer to class instance containing callback functions.

      // Override copy & assignment operator privately to disable class copying.
      CSQLClient(const CSQLClient &)
        : CSync(1,1),
          CThread(1),
          CMaCI(NULL, 0, 0, ""),
          iSQLDataSequence(),
          iSQLCommandSequence(),
          iLastSQLData(),
          iSQLClientCallback() {}
      CSQLClient &operator=(const CSQLClient &) { return *this; }
    };


  }
}

#endif //_MACI_INTERFACE_SQLCLIENT_HPP_
